#include "Busquedas.h"
#include "../interprete.h"
#include "../implementacioninterprete.h"
#include <map>

using std::cout;
using std::endl;

void ImprimirListaDocumentos(ListaDocumentos* lista){
	int tamanio = lista->size();
	for(int i=0;i<lista->size();i++){
		int temp = lista->at(i);
		printf("Doc: %d\n",temp);
	}
	
}




/********************************************************************************************/
ListaDocumentos* Buscador::RealizarBusqueda( string strBusqueda )
{
	ListaDocumentos *retLst = NULL;
	ListaDocumentos *retLstTmp = NULL;
	int pos_ini = 0, pos_fin = 0;
	int pos_and = 0, pos_or = 0, pos_operador;
	

	string textoBusqueda = tolower(strBusqueda.c_str());
	string frase_izq, frase_der, operador, palabra;	
	
	int pos_min;
	trim(textoBusqueda);
	pos_ini = textoBusqueda.find("(", pos_fin);
	pos_fin = textoBusqueda.find(")", pos_ini);
	pos_and = textoBusqueda.find("&", pos_and );
	pos_or = textoBusqueda.find("|", pos_or);
	
		//al principio
		if( ((pos_ini!=string::npos)&&(pos_fin!=string::npos))&&((( pos_ini < pos_or) && (pos_or!=string::npos))||(( pos_ini < pos_and)&&(pos_and!=string::npos)))) // es del tipo ( ...and/or... ) and/or ....
																//o del tipo (...and/or...)
		{
			frase_izq = textoBusqueda.substr( pos_ini+1, pos_fin-pos_ini-1 );//aca me queda el interior de los ( ... )
			//cout<<frase_izq<<endl;
			pos_and = textoBusqueda.find("&", pos_fin );//busco el operador despues del )
			pos_or = textoBusqueda.find("|", pos_fin );//busco el operador despues del )
			
			if( pos_and == string::npos && pos_or == string::npos ) // solo tenia un ( ... and/or... ) 
				return RealizarBusqueda( frase_izq );
				
			if ( (( pos_or == string::npos ) && ( pos_and != string::npos ) ) || 
				 ( ( pos_or != string::npos ) && ( pos_and != string::npos ) && ( pos_and<pos_or) ) )
			{
				operador = "&";
				pos_operador = pos_and;
			}
			else if ( (( pos_or != string::npos ) && ( pos_and == string::npos ) ) || 
				      ( ( pos_or != string::npos ) && ( pos_and != string::npos ) && ( pos_or<pos_and) ) )
			{
				operador = "|";
				pos_operador = pos_or;
			}
			
			frase_der = textoBusqueda.substr( pos_operador+operador.size(), textoBusqueda.size() - pos_operador+operador.size() );
			//cout<<frase_der<<endl;
			retLstTmp = RealizarBusqueda( frase_der );
			retLst = RealizarBusqueda( frase_izq );
			
			if( retLstTmp==NULL && retLst!=NULL ) 
				return retLst;
			if( retLstTmp!=NULL && retLst==NULL ) 
				return retLstTmp;
			
			if( operador=="&")
				return Interseccion(retLstTmp, retLst );
			else
				return Union(retLstTmp, retLst );
		}
		else if( ((pos_ini!=string::npos)&&(pos_fin!=string::npos))
			     && (((pos_or!=string::npos) && (pos_ini > pos_or)) 
			     || ((pos_and!=string::npos)&&( pos_ini > pos_and))) ) // es del tipo ... and/or.... ( ...and/or... ) 
		{
			if ( ( ( pos_and != string::npos && pos_or != string::npos ) && ( pos_and<pos_or) ) || ( pos_and != string::npos && pos_or == string::npos ) ) 
			{
				operador = "&";
				pos_operador = pos_and;
			}
			else if ( ( ( pos_and != string::npos && pos_or != string::npos ) && ( pos_or<pos_and) ) || ( pos_and == string::npos && pos_or != string::npos ) ) 
			{
				operador = "|";
				pos_operador = pos_or;
			}

			frase_izq = textoBusqueda.substr( pos_ini+1, pos_operador-1 );//aca me queda el interior de los ( ... )
			trim(frase_izq);
			pos_and = textoBusqueda.find("&", pos_fin );//busco el operador despues del )
			pos_or = textoBusqueda.find("|", pos_fin );//busco el operador despues del )
			
			if( pos_and == string::npos && pos_or == string::npos ) // solo tenia un ( ... and/or... ) 
				return RealizarBusqueda( frase_izq );
			
			frase_der = textoBusqueda.substr( pos_operador+operador.size(), textoBusqueda.size() - pos_operador+operador.size());
			
			if( !frase_der.empty() )
				retLstTmp = RealizarBusqueda( frase_der );
				
			retLst = RealizarBusqueda( frase_izq );
			
			if( retLstTmp==NULL && retLst!=NULL ) 
				return retLst;
			if( retLstTmp!=NULL && retLst==NULL ) 
				return retLstTmp;
			
			if( operador=="&")
				return Interseccion(retLstTmp, retLst );
			else
				return Union(retLstTmp, retLst );
		}
		else if( ((pos_ini==string::npos)&&(pos_fin==string::npos)) 
				 && ( pos_and != string::npos || pos_or != string::npos ) )  // es del tipo ... and/or ...and/or ... o sea sin ()
		{
			if ( ( ( pos_and != string::npos && pos_or != string::npos ) && ( pos_and<pos_or) ) || ( pos_and != string::npos && pos_or == string::npos ) )
			{
				operador = "&";
				pos_operador = pos_and;
			}
			else if ( ( ( pos_and != string::npos && pos_or != string::npos ) && ( pos_or<pos_and) ) || ( pos_and == string::npos && pos_or != string::npos ) )
			{
				operador = "|";
				pos_operador = pos_or;
			}
			
			frase_izq = textoBusqueda.substr( 0, pos_operador );//aca me queda el interior antes del operador
			frase_der = textoBusqueda.substr( pos_operador+operador.size(), textoBusqueda.size() - pos_operador+operador.size());
			
			if( !frase_der.empty() )
				retLstTmp = RealizarBusqueda( frase_der );

			retLst = RealizarBusqueda( frase_izq );

			if( retLstTmp==NULL && retLst!=NULL ) 
				return retLst;
			if( retLstTmp!=NULL && retLst==NULL ) 
				return retLstTmp;
			
			if( operador=="&")
				return Interseccion(retLstTmp, retLst );
			else
				return Union(retLstTmp, retLst );
		}
	
	//sino es busqueda comun
	return BuscarPalabra( textoBusqueda );
}
/********************************************************************************************/
ListaDocumentos* Buscador::BuscarPalabra( string palabra )
{
	InvertedIndex ii;
	ListaDocumentos* ret;
	bool isNot = false;
	int _posNot;
	
	_posNot = palabra.find("!", 0);
	
	if( _posNot!=string::npos)
	{
		palabra = palabra.erase(_posNot, 1);
		isNot=true;
	}
	
	//CUCHO WAS HERE
	interprete *interp = NULL;
	interp = new implementacionInterprete();
	palabra = interp->filtrarPalabra(palabra);
			
	//si la palabra no es stopword le aplico stemming y la busco con su 
	//offset
	if(!interp->esStopWord(palabra))
		palabra = interp->aplicarStemming(palabra);
	else //JPG
	{
		delete interp;
		return NULL;
	}
	//END CUCHO WAS HERE	

	InvertedIndexData iid(palabra.size(), palabra.c_str(), 0);
	ii.search(iid);
	unsigned int docCant = iid.getDocumentsCount();
		
	if (docCant>0)
	{
		ret = new ListaDocumentos();		
		for(unsigned int i = 0; i < docCant; i++)
		{
			std::pair<bool, unsigned int> docId = iid.getIdOfDocument(i);
			if(docId.first)
				ret->push_back(docId.second);
		}
		
	}
	else
	{
		ret = NULL;
	}

	if(isNot)
		ret = Complemento(ret);
		
	//CUCHO WAS HERE
    	
	if(interp!=NULL){delete(interp); interp=NULL;	}
	//END CUCHO WAS HERE
	
	//ImprimirListaDocumentos(ret);
	return ret;
}



/********************************************************************************************/
ListaDocumentos*  Buscador::Interseccion( ListaDocumentos* retLstTmp, ListaDocumentos* retLst )
{
	
	//ImprimirListaDocumentos(retLstTmp);
//	ImprimirListaDocumentos(retLst);
	
	
	ListaDocumentos* ret, *base, *auxiliar;
	ListaDocumentos::iterator itBase, itAux;
	
	ret = new ListaDocumentos();
	
	if (retLstTmp->size() < retLst->size() )
	{
		base = retLstTmp;
		auxiliar = retLst;
	}
	else
	{
		base = retLst;
		auxiliar = retLstTmp;
	}
	
	while( !base->empty() )
	{
		itBase = base->begin();
		for( itAux = auxiliar->begin(); itAux!=auxiliar->end(); itAux++)
		{
			if( (*itBase) == (*itAux) )
			{
				ret->push_back(*itBase);				
				break;
			}
		}
		base->erase(itBase);
	}
	delete base;
	base = NULL;
	delete auxiliar;
	auxiliar = NULL;
	
	
	//ImprimirListaDocumentos(ret);
	
	return ret;
}
/********************************************************************************************/

ListaDocumentos*  Buscador::Complemento( ListaDocumentos* retLst )
{
	ListaDocumentos* ret;
	ListaDocumentos::iterator itAux;
	bool encontrado = false;
		
	ret = new ListaDocumentos();	
	interprete *interp = new implementacionInterprete();
	
	int ultimoIdHash = interp->obtenerUltimoIdHash();
	delete(interp);
	interp = NULL;
	for(int nDoc = 1; nDoc <=ultimoIdHash; nDoc++ )
	{
		if(retLst!=NULL)
			for( itAux = retLst->begin(); itAux!=retLst->end(); itAux++)
			{
				if( nDoc == (*itAux) )
				{
					encontrado = true;
					retLst->erase(itAux);
					break;
				}
			}
			
		if(encontrado)
		{
			encontrado=false;
			continue;
		}
		else
			ret->push_back(nDoc);
	}
	delete interp;
	delete retLst;
	retLst = NULL;
	return ret;
}
/********************************************************************************************/
ListaDocumentos*  Buscador::Union( ListaDocumentos* retLstTmp, ListaDocumentos* retLst )
{
//	ImprimirListaDocumentos(retLstTmp);
//	ImprimirListaDocumentos(retLst);
	
	ListaDocumentos *base, *auxiliar;
	ListaDocumentos::iterator itBase, itAux;
	
	if (retLstTmp->size() > retLst->size() )
	{
		base = retLstTmp;
		auxiliar = retLst;
	}
	else
	{
		base = retLst;
		auxiliar = retLstTmp;
	}
	
	while( !auxiliar->empty() )
	{
		itAux = auxiliar->begin();
		for( itBase = base->begin(); itBase!=base->end(); itBase++)
		{
			if( (*itBase) == (*itAux) )
				break;
			else if( (*itBase) > (*itAux) )
			{
				base->insert(itBase, *itAux);
				break;
			}
		}
		if(itBase==base->end())
			base->push_back(*itAux);
			
		auxiliar->erase(itAux);
	}
	delete auxiliar;
	auxiliar = NULL;

	//ImprimirListaDocumentos(base);
	return base;
}
/********************************************************************************************/
void Buscador::trim(string& cadena)
{
	int pos=0;
	
	while((pos=cadena.find(" ", pos))!=string::npos)
		cadena.erase(pos, 1);
}
/********************************************************************************************/


//CUCHO was here 21-11-2008
vector<string> split(string s, string e) {
 vector<string> ret;
 int iPos = s.find(e, 0);
 int iPit = e.length();
 while(iPos>-1) {
   if(iPos!=0)
     ret.push_back(s.substr(0,iPos));
   s.erase(0,iPos+iPit);  
   iPos = s.find(e, 0);    
 }
 if(s!="")
   ret.push_back(s);
 return ret;
}

unsigned int buscarIdDocEnInvertedIndexData(InvertedIndexData iid,int idDocAbuscar){
	 unsigned int docCantInterna = iid.getDocumentsCount();
	 for(unsigned int i = 0; i < docCantInterna; i++)
	 {
				std::pair<bool, unsigned int> docIdInterno = iid.getIdOfDocument(i);
				if(docIdInterno.first)
				{
					if(docIdInterno.second == idDocAbuscar)
						return true;
				} 
     }	
     return false;
}

//retorna la lista de documentos segun la frase pasada por parametro
ListaDocumentos* Buscador::RealizarBusquedaAvanzada(string frase){
	
	ListaDocumentos *listaARetornar = new ListaDocumentos();
	
	if(frase.length()>0){
		interprete *interp = new implementacionInterprete();
		
		//tengo que partir la frase en palabras
		//en teoria viene separada por espacios
		std::vector<std::string> words;
	    words = split(frase," ");
		vector<std::string>::iterator it;
		InvertedIndex ii;
		unsigned int docCant;
		int offset = 0;
		typedef map<int,InvertedIndexData> DocsEncontrados;
	    DocsEncontrados docEnc;
		
		//para cada palabra la busco en los documentos
		for ( it=words.begin() ; it < words.end(); it++ ){
			string palabra = *it;
			trim(palabra);
			//le aplico los filtros necesarios
			palabra = interp->filtrarPalabra(palabra);
			
			//si la palabra no es stopword le aplico stemming y la busco con su 
			//offset
			if(!interp->esStopWord(palabra)){
				palabra = interp->aplicarStemming(palabra);
			  
				InvertedIndexData iid(palabra.size(), palabra.c_str(), 0);
	            ii.search(iid);
	            docCant =  iid.getDocumentsCount();
	           	if(docCant>0){
	           		//inserto el documento en el hash de datos con el offset de la palabra
	           		//en la frase y todos los datos de la busqueda
					docEnc.insert(pair<int,InvertedIndexData>(offset,iid));
				}
				//si una palabra no la encontro la frase no esta en ningun documento
				else{
					//limpio el map y retorno una lista de docs vacia
					docEnc.clear();
					return listaARetornar;
				}
				offset++;
			}
			//offset++;
		}
		
	//ahora hago la interseccion de los documentos para ver en cuales estan las palabras
	//con su offset puede incluso que la frase no este completa en ningun documento
   
    //busco el primer elemento para comparar los ids contenidos en el
    // y ver que que todas las palabras esten en al menos un documento
   ListaDocumentos temp; //servira para guardar los ids de los docs con frase completa
   DocsEncontrados::const_iterator itu = docEnc.begin();
   
   InvertedIndexData primero = itu->second;
   docCant = primero.getDocumentsCount();
   
   
   if(docEnc.size() == 1){
   	 for(unsigned int t=0;t< docCant;t++){
   	 	listaARetornar->push_back(primero.getIdOfDocument(t).second);
  	 }
  	 return listaARetornar;
   }
   
   
   //itero por todos los docs del primer elemento para ver que matchee en todos los los docs restantes
   for (unsigned int j = 0 ; j < docCant ; j++){
      	//este docId lo debo buscar en todos los docs
   		std::pair<bool, unsigned int> docId = primero.getIdOfDocument(j);
   		//busco en todos los documentos
   		DocsEncontrados::const_iterator it = docEnc.begin();
   		++it;//me posiciono en el segundo doc, el primero ya lo tengo
   		bool deboAgregarId = false;
   	    for(; it != docEnc.end(); it++)
	    {
	       InvertedIndexData iid = it->second;
	       bool test = buscarIdDocEnInvertedIndexData(iid,docId.second);
	       if(!test){
	       	 deboAgregarId = false;
	         break;	
          }
          deboAgregarId = true;
	    }
	    if(deboAgregarId)
	    	 temp.push_back(j);
   }
  
   //HASTA ACA ESTA PROBADO QUE FUNCIONA :)
   //EN temp tengo los ids de los documentos que tengo que verificar el offset
   //de cada palabra para la frase completa
   
   //--------- SECCION CRITICA QUE NO PROBE
    //finalmente debo ver si en los documentos en que encuentro las palabras estan con 
    //el offset de la frase que me pasaron
    for(unsigned int i=0; i < temp.size(); i++){
    	unsigned int posDocumento = temp[i];
    	unsigned int docId=primero.getIdOfDocument(posDocumento).second;
    	//Creo lista con posiciones donde aparece palabra 0 en doc[posDocumento]
    	unsigned int occCount= primero.getNumberOfOccurrencesInDocument(posDocumento).second;
		bool *valid=new bool[occCount];
		unsigned int occurrenciasDescartadas=0;
		for(int k=0;k<occCount;k++)
			valid[k]=true;
    	//levanto el map de docus encontrados y para cada palabra me fijo que 
    	//las demas esten al offset siguiente   	
    	DocsEncontrados::const_iterator it = docEnc.begin();
    	it++;
   		while((it != docEnc.end())&&(occurrenciasDescartadas<occCount))
	    {
			int offset2 = it->first;
			InvertedIndexData iid = it->second;
			//Busco el documento en el iid
			unsigned int docCantInterna = iid.getDocumentsCount();
			unsigned int s = 0;
			bool termino=false;
			while((s < docCantInterna)&&(!termino))
			{
				std::pair<bool, unsigned int> docIdInterno = iid.getIdOfDocument(s);
				unsigned int idTemporal = docIdInterno.second;
				if(idTemporal == docId){
					termino=true;
				}
				else{
					s++;
				}
					
			}
	
			//verifico que el offset de la palabra este en el documento
			//Busco en la lista de posiciones una que supere por la pos req a la original
			
			unsigned int k = 0;
			while(k < occCount){
				unsigned int occIdPrimero = primero.getOccurrenceInDocument(posDocumento,k).second;
				bool termino2=false;
				unsigned int CantOccInterna = iid.getNumberOfOccurrencesInDocument(i).second;
				unsigned int l = 0;
				//Recorro las ocurrencias en iid para ver si pal de iid esta en posicion correcta con respecto a occ de primero
				while((l < CantOccInterna)&&(!termino2)){
					unsigned int occIdInterno = iid.getOccurrenceInDocument(s,l).second;
					if(occIdPrimero+offset2==occIdInterno){
						termino2=true;
					}
					l++;
				}
				//Si ninguna de las ocurrencias estaba en pos correcta elimino esa ocurrencia de primero como posible.
				if((termino2==false)&&(valid[k]==true)){
					valid[k]=false;
					occurrenciasDescartadas++;
				}
				k++;
			}
			it++;
		}
	    //SE VERIFICA QUE LA FRASE ESTA CON LOS OFFSET
	    if(occurrenciasDescartadas<occCount)
	    	listaARetornar->push_back(docId);
   	}

     //---FIN SECCION CRITICA
     
     //liquido al interprete	
     if(interp) delete(interp);
     interp = NULL;
     
     //liquido el map
     docEnc.clear();
   }
   return listaARetornar;
}

//END CUCHO was here 21-11-2008




void Buscador::eliminarCorchetes(string &strBusqueda){
	int pos = strBusqueda.find('\[');
	int pos2=0;
	while(pos!=string::npos){
		pos2 = strBusqueda.find('\]');
		//Guardar palabra entre corchetes en lista
		lpo.push_back (strBusqueda.substr(pos+1,pos2-pos-1));

		strBusqueda.erase(pos,pos2-pos+1);
		pos = strBusqueda.find("\[");
	}

	/*cantCorchetes=lpo.size();
	/*for(int su=0; su<cantCorchetes;su++){
		std::cout<<lpo[su]<<"\n";
	}*/
}

void Buscador::ordenarPorCorchetes(ListaDocumentos* listaDocs){
	cantCorchetes=lpo.size();//Guardo la cantidad de corchetes
	if((cantCorchetes>0)&&(listaDocs->size()>0)){
		
		//Creo listado con las posiciones del primero de cada numero de hits
		int *primeros=new int[cantCorchetes+1];
		for(int su=0; su<=cantCorchetes;su++){
			primeros[su]= 0;
		}
			
		//Creo listado con las posiciones dentro de cada vector docs
		int *posiciones=new int[cantCorchetes];
		for(int su=0; su<cantCorchetes;su++){
			posiciones[su]= 0;
		}
		
		//Cargo listado con iid de palabras
		ListaDocumentos** iids=new ListaDocumentos*[cantCorchetes]; 
		for(int su=0; su<cantCorchetes;su++){
			iids[su]= this->BuscarPalabra( lpo[su] );
		}
				
		ListaDocumentos::iterator itLD;
		bool finish;
		int cantHits;
		unsigned int nroDoc;
		unsigned int nroDocActual;
		unsigned int nroDocld;
		unsigned int aux;
		for( itLD = listaDocs->begin(); itLD!=listaDocs->end(); itLD++)
		{
			cantHits=0;
			//Obtengo documento de listaDocs
			nroDocld=*itLD;
			std::cout<<nroDocld<<std::endl;
			//Busco la cantidad de palabras orden aparecen en doc.
			for(int su=0; su<cantCorchetes;su++){
				//Como los docs estan ordenados de men a mayor busco hasta encontrar mayor
				if((iids[su]!=NULL)&&(posiciones[su]<(*iids[su]).size())){
					finish=false;
					while(!finish){
						nroDoc=(*iids[su])[posiciones[su]];
						if(nroDoc>nroDocld)
							finish=true;
						else{
							posiciones[su]++;
							if(posiciones[su]==(*iids[su]).size())
								finish=true;
						}
							
						if(nroDoc==nroDocld){
							finish=true;
							cantHits++;
						}
					}
				}
			}
			//Reasigno posicion segun cantidad de hits TODO
			if(cantHits>0){
				aux=(*listaDocs)[primeros[0]];
				(*listaDocs)[primeros[0]]=*itLD;
				*itLD=aux;
				//swapeo los primeros 
				for(int sa=0; sa<cantHits;sa++){
					aux=(*listaDocs)[primeros[sa]];
					(*listaDocs)[primeros[sa]]=(*listaDocs)[primeros[sa+1]];
					(*listaDocs)[primeros[sa+1]]=aux;
					primeros[sa]++;
				}
			}
		}
		
		delete[] primeros;
		delete[] posiciones;
		for(int su=0; su<cantCorchetes;su++){
			delete iids[su];
		}
		delete[] iids;
	}
}

















